﻿using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
using XRM.Security.DataAccess.Interfaces;

namespace XRM.Security.WebUI.Helpers
{
    public static class CertificateHelper
    {
        /// <summary>
        /// Пытаемся представить массив байт в виде сертификата. Массив может представлять сертификат в формате cer и p7b
        /// </summary>
        /// <param name="certByteArr">Массив байт, который будем пытаться представить в виде массива байт.</param>
        /// <param name="index">Порядковый номер сертификата (0 или 1) </param>
        /// <returns>Сертификат, который соответствует переданному массиву.</returns>
        /// <exception cref="System.Security.Cryptography.CryptographicException">Возникает в случае невозможности распарсить сертификат.</exception>
        public static X509Certificate2 Parse(byte[] certByteArr, int index)
        {
            X509Certificate2 certificate;
            var certContentType = X509Certificate2.GetCertContentType(certByteArr);
            if (certContentType == X509ContentType.Cert)
            {
                certificate = new X509Certificate2(certByteArr);
            }
            else
            {
                try
                {
                    var cms = new SignedCms();
                    cms.Decode(certByteArr);
                    if (index < cms.Certificates.Count)
                        certificate = cms.Certificates[index];
                    else
                        certificate = cms.Certificates[0];
                }
                catch (Exception)
                {
                    var s = System.Text.Encoding.ASCII.GetString(certByteArr);
                    var sr = new StringReader(s);
                    var line = sr.ReadLine();
                    var totalLine = string.Empty;
                    while (line != null)
                    {
                        if ((!line.Contains("BEGIN CERTIFICATE")) && (!line.Contains("END CERTIFICATE")))
                        {
                            totalLine = totalLine + line;
                        }
                        line = sr.ReadLine();
                    }
                    var decode = Convert.FromBase64String(totalLine);
                    var cms = new SignedCms();
                    cms.Decode(decode);
                    if (index < cms.Certificates.Count)
                        certificate = cms.Certificates[index];
                    else
                        certificate = cms.Certificates[0];
                }
            }
            return certificate;
        }

        public static X509Certificate2[] Parse( byte[] certByteArr )
        {
            var certContentType = X509Certificate2.GetCertContentType( certByteArr );
            if ( certContentType == X509ContentType.Cert )
            {
                return new[] {new X509Certificate2( certByteArr )};
            }
            try
            {
                var cms = new SignedCms();
                cms.Decode( certByteArr );
                return cms.Certificates.Cast<X509Certificate2>().ToArray();
            }
            catch ( Exception )
            {
                var s = System.Text.Encoding.ASCII.GetString( certByteArr );
                var sr = new StringReader( s );
                var line = sr.ReadLine();
                var totalLine = string.Empty;
                while ( line != null )
                {
                    if ( ( !line.Contains( "BEGIN CERTIFICATE" ) ) && ( !line.Contains( "END CERTIFICATE" ) ) )
                    {
                        totalLine = totalLine + line;
                    }
                    line = sr.ReadLine();
                }
                var decode = Convert.FromBase64String( totalLine );
                var cms = new SignedCms();
                cms.Decode( decode );
                return cms.Certificates.Cast<X509Certificate2>().ToArray();
            }
        }

        public static X509Certificate2 GetUserCertificate( X509Certificate2[] certificates )
        {
            return certificates.FirstOrDefault( s => s.GetNameInfo( X509NameType.SimpleName, true ) != s.GetNameInfo( X509NameType.SimpleName, false ) );
        }

        public static X509Certificate2 GetRootCertificate( X509Certificate2[] certificates )
        {
            return certificates.FirstOrDefault( s => s.GetNameInfo( X509NameType.SimpleName, true ) == s.GetNameInfo( X509NameType.SimpleName, false ) );
        }

        public static string GetUserName (this X509Certificate2 certificate )
        {
            return certificate.GetNameInfo( X509NameType.SimpleName, false );
        }

        public static string GetIssuerOrganizationName( this X509Certificate2 certificate )
        {
            return GetIssuerName( certificate.Issuer );
        }

        public static void SetProperties( this ICertificate certModel, X509Certificate2 certificate, string fileName )
        {
            certModel.FromDate = certificate.NotBefore;
            certModel.ToDate = certificate.NotAfter;
            certModel.Thumbprint = certificate.Thumbprint;
            certModel.SerialNumber = certificate.SerialNumber;
            certModel.FileData = certificate.RawData;
            certModel.UploadDate = DateTime.Now;
            certModel.FileName = fileName;
        }

        public static string GetIssuerName( string issuer )
        {
            if (issuer == null)
                return null;

            string cn = null;
            foreach (var part in issuer.Split( new[] { ',' } ) )
            {
                if ( part.Contains( "O=" ) )
                    return GetPartValue(part);
                if ( part.Contains( "CN=" ) )
                    cn = GetPartValue( part );
            }
            return cn;
        }

        private static string GetPartValue(string str)
        {
            var parts = str.Split( new[] { '=' } );
            if ( parts.Length < 2 )
                return null;
            var value = parts[1].Trim();
            if ( value[0] == '"' && value[value.Length - 1] == '"' )
                value = value.Substring( 1, value.Length - 2 );
            while (value.Contains("\"\""))
            {
                value = value.Replace( "\"\"", "\"" );
            }
            return value;
        }

        public static string ParseSubject(string subject, string param)
        {
            string result = "";

            if (string.IsNullOrEmpty(subject))
                return result;

            string[] subjects = subject.Split(',');
            foreach (string s in subjects)
            {
                string[] paramValue = s.Trim().Split('=');
                if (paramValue.Length != 2)
                    continue;

                if (paramValue[0].ToUpper().Equals(param.ToUpper()))
                {
                    result = paramValue[1];
                    if ( result[0] == '"' )
                        result = result.Substring( 1, result.Length - 2 );
                    break;
                }
            }

            return result;
        }

    }
}